/**
 * 目录大纲支持插件
 * @file
 * @since 1.3.0
 */
UE.plugin.register("section", function () {
    /* 目录节点对象 */
    function Section(option) {
        this.tag = "";
        (this.level = -1), (this.dom = null);
        this.nextSection = null;
        this.previousSection = null;
        this.parentSection = null;
        this.startAddress = [];
        this.endAddress = [];
        this.children = [];
    }

    function getSection(option) {
        var section = new Section();
        return utils.extend(section, option);
    }

    function getNodeFromAddress(startAddress, root) {
        var current = root;
        for (var i = 0; i < startAddress.length; i++) {
            if (!current.childNodes) return null;
            current = current.childNodes[startAddress[i]];
        }
        return current;
    }

    var me = this;

    return {
        bindMultiEvents: {
            type: "aftersetcontent afterscencerestore",
            handler: function () {
                me.fireEvent("updateSections");
            }
        },
        bindEvents: {
            /* 初始化、拖拽、粘贴、执行setcontent之后 */
            ready: function () {
                me.fireEvent("updateSections");
                domUtils.on(me.body, "drop paste", function () {
                    me.fireEvent("updateSections");
                });
            },
            /* 执行paragraph命令之后 */
            afterexeccommand: function (type, cmd) {
                if (cmd == "paragraph") {
                    me.fireEvent("updateSections");
                }
            },
            /* 部分键盘操作，触发updateSections事件 */
            keyup: function (type, e) {
                var me = this,
                    range = me.selection.getRange();
                if (range.collapsed != true) {
                    me.fireEvent("updateSections");
                } else {
                    var keyCode = e.keyCode || e.which;
                    if (keyCode == 13 || keyCode == 8 || keyCode == 46) {
                        me.fireEvent("updateSections");
                    }
                }
            }
        },
        commands: {
            getsections: {
                execCommand: function (cmd, levels) {
                    var levelFn = levels || ["h1", "h2", "h3", "h4", "h5", "h6"];

                    for (var i = 0; i < levelFn.length; i++) {
                        if (typeof levelFn[i] == "string") {
                            levelFn[i] = (function (fn) {
                                return function (node) {
                                    return node.tagName == fn.toUpperCase();
                                };
                            })(levelFn[i]);
                        } else if (typeof levelFn[i] != "function") {
                            levelFn[i] = function (node) {
                                return null;
                            };
                        }
                    }

                    function getSectionLevel(node) {
                        for (var i = 0; i < levelFn.length; i++) {
                            if (levelFn[i](node)) return i;
                        }
                        return -1;
                    }

                    var me = this,
                        Directory = getSection({level: -1, title: "root"}),
                        previous = Directory;

                    function traversal(node, Directory) {
                        var level,
                            tmpSection = null,
                            parent,
                            child,
                            children = node.childNodes;
                        for (var i = 0, len = children.length; i < len; i++) {
                            child = children[i];
                            level = getSectionLevel(child);
                            if (level >= 0) {
                                var address = me.selection
                                        .getRange()
                                        .selectNode(child)
                                        .createAddress(true).startAddress,
                                    current = getSection({
                                        tag: child.tagName,
                                        title: child.innerText || child.textContent || "",
                                        level: level,
                                        dom: child,
                                        startAddress: utils.clone(address, []),
                                        endAddress: utils.clone(address, []),
                                        children: []
                                    });
                                previous.nextSection = current;
                                current.previousSection = previous;
                                parent = previous;
                                while (level <= parent.level) {
                                    parent = parent.parentSection;
                                }
                                current.parentSection = parent;
                                parent.children.push(current);
                                tmpSection = previous = current;
                            } else {
                                child.nodeType === 1 && traversal(child, Directory);
                                tmpSection &&
                                tmpSection.endAddress[tmpSection.endAddress.length - 1]++;
                            }
                        }
                    }

                    traversal(me.body, Directory);
                    return Directory;
                },
                notNeedUndo: true
            },
            movesection: {
                execCommand: function (cmd, sourceSection, targetSection, isAfter) {
                    var me = this,
                        targetAddress,
                        target;

                    if (!sourceSection || !targetSection || targetSection.level == -1)
                        return;

                    targetAddress = isAfter
                        ? targetSection.endAddress
                        : targetSection.startAddress;
                    target = getNodeFromAddress(targetAddress, me.body);

                    /* 判断目标地址是否被源章节包含 */
                    if (
                        !targetAddress ||
                        !target ||
                        isContainsAddress(
                            sourceSection.startAddress,
                            sourceSection.endAddress,
                            targetAddress
                        )
                    )
                        return;

                    var startNode = getNodeFromAddress(
                        sourceSection.startAddress,
                        me.body
                        ),
                        endNode = getNodeFromAddress(sourceSection.endAddress, me.body),
                        current,
                        nextNode;

                    if (isAfter) {
                        current = endNode;
                        while (
                            current &&
                            !(
                                domUtils.getPosition(startNode, current) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.previousSibling;
                            domUtils.insertAfter(target, current);
                            if (current == startNode) break;
                            current = nextNode;
                        }
                    } else {
                        current = startNode;
                        while (
                            current &&
                            !(
                                domUtils.getPosition(current, endNode) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.nextSibling;
                            target.parentNode.insertBefore(current, target);
                            if (current == endNode) break;
                            current = nextNode;
                        }
                    }

                    me.fireEvent("updateSections");

                    /* 获取地址的包含关系 */
                    function isContainsAddress(startAddress, endAddress, addressTarget) {
                        var isAfterStartAddress = false,
                            isBeforeEndAddress = false;
                        for (var i = 0; i < startAddress.length; i++) {
                            if (i >= addressTarget.length) break;
                            if (addressTarget[i] > startAddress[i]) {
                                isAfterStartAddress = true;
                                break;
                            } else if (addressTarget[i] < startAddress[i]) {
                                break;
                            }
                        }
                        for (var i = 0; i < endAddress.length; i++) {
                            if (i >= addressTarget.length) break;
                            if (addressTarget[i] < startAddress[i]) {
                                isBeforeEndAddress = true;
                                break;
                            } else if (addressTarget[i] > startAddress[i]) {
                                break;
                            }
                        }
                        return isAfterStartAddress && isBeforeEndAddress;
                    }
                }
            },
            deletesection: {
                execCommand: function (cmd, section, keepChildren) {
                    var me = this;

                    if (!section) return;

                    function getNodeFromAddress(startAddress) {
                        var current = me.body;
                        for (var i = 0; i < startAddress.length; i++) {
                            if (!current.childNodes) return null;
                            current = current.childNodes[startAddress[i]];
                        }
                        return current;
                    }

                    var startNode = getNodeFromAddress(section.startAddress),
                        endNode = getNodeFromAddress(section.endAddress),
                        current = startNode,
                        nextNode;

                    if (!keepChildren) {
                        while (
                            current &&
                            domUtils.inDoc(endNode, me.document) &&
                            !(
                                domUtils.getPosition(current, endNode) &
                                domUtils.POSITION_FOLLOWING
                            )
                            ) {
                            nextNode = current.nextSibling;
                            domUtils.remove(current);
                            current = nextNode;
                        }
                    } else {
                        domUtils.remove(current);
                    }

                    me.fireEvent("updateSections");
                }
            },
            selectsection: {
                execCommand: function (cmd, section) {
                    if (!section && !section.dom) return false;
                    var me = this,
                        range = me.selection.getRange(),
                        address = {
                            startAddress: utils.clone(section.startAddress, []),
                            endAddress: utils.clone(section.endAddress, [])
                        };
                    address.endAddress[address.endAddress.length - 1]++;
                    range.moveToAddress(address).select().scrollToView();
                    return true;
                },
                notNeedUndo: true
            },
            scrolltosection: {
                execCommand: function (cmd, section) {
                    if (!section && !section.dom) return false;
                    var me = this,
                        range = me.selection.getRange(),
                        address = {
                            startAddress: section.startAddress,
                            endAddress: section.endAddress
                        };
                    address.endAddress[address.endAddress.length - 1]++;
                    range.moveToAddress(address).scrollToView();
                    return true;
                },
                notNeedUndo: true
            }
        }
    };
});
